﻿// ***********************************************************************
// Assembly         : Tx.OCC
// Author           : tianteng
// Created          : 09-09-2019
//
// Last Modified By : tianteng
// Last Modified On : 09-09-2019
// ***********************************************************************
// <copyright file="OccGpEx.cs" company="北京腾雪科技有限责任公司">
//     Copyright © 北京腾雪科技有限责任公司 2022
// </copyright>
// <summary></summary>
// ***********************************************************************
using Occt;
using System;

namespace Tx.OCC
{
    /// <summary>
    /// Class OccGpEx.
    /// </summary>
    public static class OccGpEx
    {
        /// <summary>
        /// Gets the normal.
        /// </summary>
        /// <param name="origin">The origin.</param>
        /// <param name="pxAxis">The px axis.</param>
        /// <param name="pyAxis">The py axis.</param>
        /// <returns>gp_XYZ.</returns>
        public static gp_XYZ GetNormal(gp_XYZ origin, gp_XYZ pxAxis, gp_XYZ pyAxis)
        {
            var v1 = pxAxis - origin;
            var v2 = pyAxis - origin;
            var v = v1.Crossed(v2);
            v.Normalize();
            return v;
        }

        /// <summary>
        /// Middles the specified PT2.
        /// </summary>
        /// <param name="pt1">The PT1.</param>
        /// <param name="pt2">The PT2.</param>
        /// <returns>gp_Pnt.</returns>
        public static gp_Pnt Middle(this gp_Pnt pt1, gp_Pnt pt2)
        {
            return (pt1 + pt2) * 0.5;
        }


        /// <summary>
        /// Middles the specified PT2.
        /// </summary>
        /// <param name="pt1">The PT1.</param>
        /// <param name="pt2">The PT2.</param>
        /// <returns>gp_XYZ.</returns>
        public static gp_XYZ Middle(this gp_XYZ pt1, gp_XYZ pt2)
        {
            return (pt1 + pt2) * 0.5;
        }

        /// <summary>
        /// Creates new ax2.
        /// </summary>
        /// <param name="location">The location.</param>
        /// <param name="xAxis">The x axis.</param>
        /// <param name="yAxis">The y axis.</param>
        /// <returns>gp_Ax2.</returns>
        public static gp_Ax2 NewAx2(gp_Pnt location, gp_Dir xAxis, gp_Dir yAxis)
        {
            var zAxis = xAxis ^ yAxis;
            return new gp_Ax2(location, zAxis, xAxis);
        }

        /// <summary>
        /// Makes the rotate.
        /// </summary>
        /// <param name="location">The location.</param>
        /// <param name="from">From.</param>
        /// <param name="to">To.</param>
        /// <param name="normal">The normal.</param>
        /// <returns>gp_Trsf.</returns>
        public static gp_Trsf MakeRotate(gp_Pnt location, gp_Vec from, gp_Vec to, gp_Vec normal)
        {
            var angle = @from.AngleWithRef(to, normal);
            var trsf = new gp_Trsf();
            trsf.SetRotation(new gp_Ax1(location, new gp_Dir(normal)), angle);
            return trsf;
        }

        #region gp_Pnt
        /// <summary>
        /// Rounds the specified PNT.
        /// </summary>
        /// <param name="pnt">The PNT.</param>
        /// <param name="digits"></param>
        /// <returns>gp_Pnt.</returns>
        public static gp_Pnt Round(this gp_Pnt pnt, int digits = 3)
        {
            return new gp_Pnt(pnt.X.Round(digits), pnt.Y.Round(digits), pnt.Z.Round(digits));
        }
        #endregion

        #region gp_Pln
        /// <summary>
        /// Rotations the specified plane.
        /// </summary>
        /// <param name="plane">The plane.</param>
        /// <returns>gp_Quaternion.</returns>
        public static gp_Quaternion Rotation(this gp_Pln plane)
        {
            return new gp_Quaternion(new gp_Mat(plane.XAxis.Direction.XYZ, plane.YAxis.Direction.XYZ, plane.Axis.Direction.XYZ));
        }
        
        /// <summary>
        /// Determines whether the specified PLN2 is equal.
        /// </summary>
        /// <param name="pln1">The PLN1.</param>
        /// <param name="pln2">The PLN2.</param>
        /// <returns><c>true</c> if the specified PLN2 is equal; otherwise, <c>false</c>.</returns>
        public static bool IsEqual(this gp_Pln pln1, gp_Pln pln2)
        {
            return pln1.Location.IsEqual(pln2.Location, double.Epsilon)
                   && pln1.Rotation().IsEqual(pln2.Rotation());
        }
        
        /// <summary>
        /// Parameterses the specified PNT.
        /// </summary>
        /// <param name="pln">The PLN.</param>
        /// <param name="pnt">The PNT.</param>
        /// <returns>gp_Pnt2d.</returns>
        public static gp_Pnt2d Parameters(this gp_Pln pln, gp_Pnt pnt)
        {
            ElSLib.Parameters(pln, pnt, out var u, out var v);
            return new gp_Pnt2d(u, v);
        }
        
        /// <summary>
        /// Values the specified uv.
        /// </summary>
        /// <param name="pln">The PLN.</param>
        /// <param name="uv">The uv.</param>
        /// <returns>gp_Pnt.</returns>
        public static gp_Pnt Value(this gp_Pln pln, gp_Pnt2d uv)
        {
            return ElSLib.Value(uv.X, uv.Y, pln);
        }
        #endregion

        #region gp_Quaternion
        /// <summary>
        /// Converts to ax3.
        /// </summary>
        /// <param name="rotation">The rotation.</param>
        /// <param name="location">The location.</param>
        /// <returns>gp_Ax3.</returns>
        public static gp_Ax3 ToAx3(this gp_Quaternion rotation, gp_Pnt location)
        {
            return new gp_Ax3(location,
                rotation.Multiply(new gp_Vec(0, 0, 1)).ToDir(),
                rotation.Multiply(new gp_Vec(1, 0, 0)).ToDir());
        }
        
        /// <summary>
        /// Converts to euler.
        /// </summary>
        /// <param name="rotation">The rotation.</param>
        /// <returns>System.ValueTuple&lt;System.Double, System.Double, System.Double&gt;.</returns>
//         public static (double yaw, double pitch, double roll) ToEuler(this gp_Quaternion rotation)
//         {
//             rotation.GetEulerAngles(gp_EulerSequence.gp_EulerAngles, out double y, out double p, out double r);
//             return (y, p, r);
//         }
        #endregion

        #region gp_Ax22d
        /// <summary>
        /// Senses the specified ax.
        /// </summary>
        /// <param name="ax">The ax.</param>
        /// <returns>System.Int32.</returns>
        public static int Sense(this gp_Ax22d ax)
        {
            return ax.YAxis.Angle(ax.XAxis) > 0 ? 1 : -1;
        }
        #endregion
    }
}
